/*UniApo
filename:   sqlstatement.h
date:       April 2010
author:     Jeffrey Geyssens
about:      A buildable UniApo object representing a SQL statement.
		    
            Using this class, one can execute a SQL statement on the database set up
            using the stored connection.
 */

#ifndef SQLSTATEMENT_H
#define SQLSTATEMENT_H
#include <iostream>
#include <string>
#include <map>
#include <list>
#include <fstream>
#include <mysql++.h>

using namespace std;

enum StatementType {
    DDL, DML, DQL
};

class SQLStatement {
protected:
    string statement;

    const string& getStatement() const {
        return statement;
    }
    StatementType type;
    void checkQuery();
    //Connect to a db.
    int connect(int dbversion);

    void setStatement(const string& statement);

    bool setStatementType();

    virtual bool format() {
    }; //Removes unneeded whitespaces from statement

    virtual void revert() {
    }; //Drops effected table from db

public:

    SQLStatement();

    virtual ~SQLStatement() {
    };
    StatementType getType();
    bool addStatement(const string& statement);
    void loadStatement(const string& statement);
    void execute();
    bool test(); //Executes Query on test_db
    void buildFile();
    void print();
    friend ostream & operator<<(ostream&, const SQLStatement&);
private:

};

/* UniApo
 * File:   sqlstatement.h
 * Author: Jeffrey Geyssens
 *
 * Created on April 26, 2010, 6:09 PM
 * This is the header file for the DBSFile object, which is loaded when a
 * DBS file is created by the user or loaded from file.
 *
 * This object is created and used by the DBSEditor.
 * Manually modifying the physical file using a OS based text editor
 * may provoke errors when reading to the object, you have been warned!
 *
 * PSQL Statements are DML statements. Instead of containing the actual
 * attribute values, they are substituted by parameters which represent
 * a (Data)ReferenceTerminal. The use of ids allows the container to store
 * strings instead of the actual objects.
 */


SQLStatement::SQLStatement() {
}




//Connecting to a db of which the dbase name is supplied as argument.
//Code stolen from Mysql++.com

int SQLStatement::connect(int dbversion) {
    //first we read the configuration file
    //it is stored in /UniApo/Saves

    char *path = NULL;
    path = getcwd(NULL, 0);
    string path2 = path;
    string::size_type loc1 = path2.find("UniApo", 0);
    stringstream absolute;


    absolute << path2.substr(0, loc1 + 6) << "/Saves/db.conf";
    string s = absolute.str();
    //Prepare to read file contents by creating expected objected
    //todo: Error checking
    string db;
    string connector;
    string host;
    string uname;
    string passw;
    //string storedb;
    //string testdb;

    //Input file stream on the file residing at tehe absolute path.
    ifstream myfile(s.c_str());



    if (myfile.is_open()) {
        //First read file, one line at a time
        //If file is correct, all objects should be given a value.

        getline(myfile, db);
        getline(myfile, connector);
        getline(myfile, host);
        getline(myfile, uname);
        getline(myfile, passw);


        if (dbversion = 1) { //test for 2nd db

            getline(myfile, db);
            getline(myfile, connector);
            getline(myfile, host);
            getline(myfile, uname);
            getline(myfile, passw);


        }





        //After reading 4 lines, close file stream.
        myfile.close();
        //Now fix the obtained variables by ommiting their prefix
        db = db.substr(3, db.size());
        connector = connector.substr(5, connector.size()); //ommit "db:"
        host = host.substr(5, host.size()); //ommit "host:"
        uname = uname.substr(6, uname.size()); //ommit "uname:"
        passw = passw.substr(6, passw.size()); //ommit "passw:"
    }
    //connect to database using file contents.
    try {
        // Establish the connection to the database server.
        mysqlpp::Connection con
                (db.c_str(), host.c_str(), uname.c_str(), passw.c_str());

        // Show initial state
        mysqlpp::Query query = con.query(statement);


        {
            mysqlpp::Transaction trans(con);
            query.execute();

            trans.commit();
            return 0;
        }
    } catch (const mysqlpp::BadQuery& er) {
        // Handle any query errors
        cerr << "Query error: " << er.what() << endl;
        return 1;
    } catch (const mysqlpp::BadConversion& er) {
        // Handle bad conversions
        cerr << "Conversion error: " << er.what() << endl <<
                "\tretrieved data size: " << er.retrieved <<
                ", actual size: " << er.actual_size << endl;
        return 2;
    } catch (const mysqlpp::Exception& er) {
        // Catch-all for any other MySQL++ exceptions
        cerr << "Error: " << er.what() << endl;
        return 3;
    }
}

bool SQLStatement::test() {
    //Executing statement on test database
    //Testing DMLStatements will work because a copy of the database structure
    //is made in the clean uniapo_test database when creating a MAP file.
    //DML Statements are used by the learner or when entering
    //sentence structure rules using the MAP editor.
    //In any event DML statements are used after the mapping of a DBS file.
    int result;
    result = this->connect(1);
    if (result == 0) {
        return true;
    }
    if (result == 1) {
        cerr << "ERROR(DMLStatement): Query Error" << endl;
        cerr << "Unidentified Query Error (check syntax)" << endl;
        return false;
    } else {
        cerr << "Error(DMLStatement): Database error" << endl;
        return false;
    }
}


//A real execution on actual storage database

void SQLStatement::execute() {
    this->connect(2);
}

bool SQLStatement::addStatement(const string& s) {
    bool result;

    this->setStatement(s);
    result = this->format();

    if (result == false)
        return false;
    //Here we have to to some more stuff

    result = this->test();

    if (result == true) {
        this->revert();
        cout << "SQL Statement Added: " << endl <<statement << endl << endl;
        return true;
    } else
        return false;


}

void SQLStatement::loadStatement(const string& s) {
    this->setStatement(s);
}

ostream & operator<<(ostream& output, const SQLStatement& data) {
    return output << data.getStatement();
}

void SQLStatement::setStatement(const string& statement) {
    this->statement = statement;
}

StatementType SQLStatement::getType() {
    return type;
}

void SQLStatement::buildFile() {
    cout << this->getStatement() << endl;

}

void SQLStatement::print() {
    cout << this->getStatement() << endl;
}

#endif // SQLSTATEMENT_H
